package com.cmes.core.utils;

import com.cmes.core.context.SpringContextHolder;
import com.cmes.core.exception.FrameworkException;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;

/**
 * Json工具类
 * <p>
 * ObjectMapper 默认使用 ServletApplicationContext 初始化的同一个，也支持自定义。
 * 在序列化及反序列化过程中，如果出现异常，默认会抛出 {@code FrameworkException} 异常。但这个要看 {@code throwEx} 的值，可以通过 {@link #throwEx} 方法手动为其赋值。
 *
 * @author Bruce.Gong
 * @since 1.0.0-SNAPSHOT
 */
@Slf4j
public class JsonUtils {

    private static ObjectMapper mapper;
    private static boolean throwEx = true;

    /**
     * 序列化
     *
     * @param value 待序列化对象
     * @return JSON字符串
     */
    public static String serializer(Object value) {
        return serializer(value, throwEx);
    }

    /**
     * 序列化
     *
     * @param value   待序列化对象
     * @param throwEx 如果序列化出现异常，是否抛出
     * @return JSON字符串
     * @throws FrameworkException 如果序列化出现异常，且 {@code throwEx} 为 true，则抛出框架异常
     */
    public static String serializer(Object value, boolean throwEx) {
        try {
            return getMapper().writeValueAsString(value);
        } catch (Exception e) {
            log.error("JSON序列化失败", e);
            if (throwEx) {
                throw new FrameworkException("JSON序列化失败");
            } else {
                return StringPool.EMPTY;
            }
        }
    }

    /**
     * 反序列化
     *
     * @param value      JSON字符串
     * @param valueClass 对象类型
     * @param <T>        对象类型
     * @return 序列化后的对象
     */
    public static <T> T deserializer(String value, Class<T> valueClass) {
        return deserializer(value, valueClass, throwEx);
    }

    /**
     * 反序列化
     *
     * @param value      JSON字符串
     * @param valueClass 对象类型
     * @param throwEx    如果序列化出现异常，是否抛出
     * @param <T>        对象类型
     * @return 序列化后的对象
     */
    public static <T> T deserializer(String value, Class<T> valueClass, boolean throwEx) {
        try {
            return getMapper().readValue(value, valueClass);
        } catch (Exception e) {
            log.error("JSON反序列化失败", e);
            if (throwEx) {
                throw new FrameworkException("JSON反序列化失败");
            } else {
                return null;
            }
        }
    }

    /**
     * 反序列化，支持泛型
     *
     * @param value         JSON字符串
     * @param typeReference 对象类型
     * @param <T>           对象类型
     * @return 序列化后的对象
     */
    public static <T> T deserializer(String value, TypeReference<T> typeReference) {
        return deserializer(value, typeReference, throwEx);
    }


    /**
     * 反序列化，支持泛型
     *
     * @param value         JSON字符串
     * @param typeReference 对象类型
     * @param throwEx       如果序列化出现异常，是否抛出
     * @param <T>           对象类型
     * @return 序列化后的对象
     */
    public static <T> T deserializer(String value, TypeReference<T> typeReference, boolean throwEx) {
        try {
            return getMapper().readValue(value, typeReference);
        } catch (Exception e) {
            log.error("JSON反序列化失败", e);
            if (throwEx) {
                throw new FrameworkException("JSON反序列化失败");
            } else {
                return null;
            }
        }
    }

    /**
     * 反序列化
     *
     * @param value JSON字符串
     * @return JsonNode
     */
    public static JsonNode deserializerAsNode(String value) {
        return deserializerAsNode(value, throwEx);
    }

    /**
     * 反序列化
     *
     * @param value   JSON字符串
     * @param throwEx 如果序列化出现异常，是否抛出
     * @return JsonNode
     */
    public static JsonNode deserializerAsNode(String value, boolean throwEx) {
        try {
            return getMapper().readTree(value);
        } catch (Exception e) {
            log.error("JSON序列化失败", e);
            if (throwEx) {
                throw new FrameworkException("JSON反序列化失败");
            } else {
                return null;
            }
        }
    }

    /**
     * 将 Class 转换为 JavaType，需要使用 Jackson 的一些特性时使用
     *
     * @param clazz 数据类型
     * @return JavaType
     */
    public static JavaType constructType(Class<?> clazz) {
        return getMapper().constructType(clazz);
    }

    /**
     * 手动定义 ObjectMapper
     *
     * @param mapper ObjectMapper
     */
    public static void customer(ObjectMapper mapper) {
        JsonUtils.mapper = mapper;
    }

    /**
     * 手动定义 ObjectMapper
     *
     * @param mapper ObjectMapper
     */
    public static void customer(ObjectMapper mapper, boolean throwEx) {
        JsonUtils.mapper = mapper;
        JsonUtils.throwEx = throwEx;
    }

    /**
     * 手动定义 ObjectMapper
     *
     * @param throwEx ObjectMapper
     */
    public static void throwEx(boolean throwEx) {
        JsonUtils.throwEx = throwEx;
    }

    /**
     * 获取 ServletApplicationContext 启动时注入的 ObjectMapper
     */
    private static ObjectMapper getMapper() {
        if (mapper == null) {
            ObjectMapper om = SpringContextHolder.getBean(ObjectMapper.class);
            mapper = om.copy();
            mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
            mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        }
        return mapper;
    }

}